//-----------------------------------------------------------------------------
// Copyright (c) 2017 Oracle and/or its affiliates.  All rights reserved.
// This program is free software: you can modify it and/or redistribute it
// under the terms of:
//
// (i)  the Universal Permissive License v 1.0 or at your option, any
//      later version (http://oss.oracle.com/licenses/upl); and/or
//
// (ii) the Apache License v 2.0. (http://www.apache.org/licenses/LICENSE-2.0)
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// TestStatements.c
//   Test suite for testing all the statement related cases.
//-----------------------------------------------------------------------------

#include "TestLib.h"

//-----------------------------------------------------------------------------
// dpiTest__callFunctionsWithError() [INTERNAL]
//   Test all public functions with the specified statement and expect the
// given error for each of them.
//-----------------------------------------------------------------------------
int dpiTest__callFunctionsWithError(dpiTestCase *testCase, dpiStmt *stmt,
        const char *expectedError)
{
    dpiStmt_bindByName(stmt, NULL, 0, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_bindByPos(stmt, 0, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_bindValueByName(stmt, NULL, 0, DPI_NATIVE_TYPE_INT64, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_bindValueByPos(stmt, 0, DPI_NATIVE_TYPE_INT64, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_define(stmt, 0, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_defineValue(stmt, 0, DPI_ORACLE_TYPE_NATIVE_INT,
            DPI_NATIVE_TYPE_INT64, 0, 0, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_executeMany(stmt, DPI_MODE_EXEC_DEFAULT, 0);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_fetch(stmt, NULL, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_fetchRows(stmt, 1, NULL, NULL, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getBatchErrorCount(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getBatchErrors(stmt, 1, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getBindCount(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getBindNames(stmt, NULL, NULL, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getFetchArraySize(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getImplicitResult(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getInfo(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getNumQueryColumns(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getQueryInfo(stmt, 1, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getQueryValue(stmt, 1, NULL, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getRowCount(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getRowCounts(stmt, NULL, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_getSubscrQueryId(stmt, NULL);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_scroll(stmt, DPI_MODE_FETCH_FIRST, 0, 0);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    dpiStmt_setFetchArraySize(stmt, 1);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest__verifyBindCount() [INTERNAL]
//   Determines the number of bind variables for the given statement and
// verifies that it matches the expected value.
//-----------------------------------------------------------------------------
int dpiTest__verifyBindCount(dpiTestCase *testCase, dpiConn *conn,
        const char *sql, uint32_t expectedCount)
{
    uint32_t count;
    dpiStmt *stmt;

    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBindCount(stmt, &count) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTestCase_expectUintEqual(testCase, count, expectedCount);
}


//-----------------------------------------------------------------------------
// dpiTest_1100_releaseTwice()
//   Prepare any statement; call dpiStmt_release() twice (error DPI-1002).
//-----------------------------------------------------------------------------
int dpiTest_1100_releaseTwice(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select * from TestLongs";
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiStmt_release(stmt);
    return dpiTestCase_expectError(testCase,
            "DPI-1002: invalid dpiStmt handle");
}


//-----------------------------------------------------------------------------
// dpiTest_1101_executeManyInvalidParams()
//   Prepare any query; call dpiStmt_executeMany() (error DPI-1013).
//-----------------------------------------------------------------------------
int dpiTest_1101_executeManyInvalidParams(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select * from TestLongs";
    const uint32_t numIters = 2;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiStmt_executeMany(stmt, DPI_MODE_EXEC_DEFAULT, numIters);
    if (dpiTestCase_expectError(testCase, "DPI-1013: not supported") < 0)
        return DPI_FAILURE;
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1102_verifyCallStmtWorksAsExp()
//   Prepare any CALL statement; call dpiStmt_getInfo() and verify that the
// isPLSQL value in the dpiStmtInfo structure is set to 1 and that the
// statementType value is set to DPI_STMT_TYPE_CALL (no error).
//-----------------------------------------------------------------------------
int dpiTest_1102_verifyCallStmtWorksAsExp(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "call proc_TestNoArgs();";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 1) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_CALL);
}


//-----------------------------------------------------------------------------
// dpiTest_1103_bindCountWithVarBinds()
//   Prepare multiple statements with differing numbers of bind variables; call 
// dpiStmt_getBindCount() and confirm that the value returned is as expected,
// for both SQL and PL/SQL statements (no error).
//-----------------------------------------------------------------------------
int dpiTest_1103_bindCountWithVarBinds(dpiTestCase *testCase,
        dpiTestParams *params)
{
    dpiConn *conn;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "select * from TestDataTypes", 0) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "select :1 from TestDataTypes", 1) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "select :1, :2, :3, :4, :5, :6, :7 from TestDataTypes", 7) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "select :1, :2, :3, :4, :5, :6, :7, :8 from TestDataTypes", 8) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "select :1, :2, :3, :4, :5, :6, :7, :8, :9, :9 from TestDataTypes",
            10) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "select :1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, "
            ":13, :14, :15, :16, :17 from TestDataTypes", 17) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "begin select :1, :1 from dual; end;", 1) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "begin select :1, :2, :3, :4, :5, :6, :7 from dual; end;", 7) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "begin select :1, :2, :3, :4, :5, :6, :7, :8, :8 from dual; end;",
            8) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "begin select :1, :2, :3, :4, :5, :6, :7, :8, :8, :9, :9, :10 "
            "from dual; end;", 10) < 0)
        return DPI_FAILURE;
    if (dpiTest__verifyBindCount(testCase, conn,
            "begin select :1, :2, :3, :4, :5, :6, :7, :8, :8, :9, :10, "
            ":11, :12, :13, :14, :15, :16, :17 from dual; end;", 17) < 0)
        return DPI_FAILURE;

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1104_bindNamesNoDuplicatesSql()
//   Prepare any statement with duplicate bind variable names; call
// dpiStmt_getBindNames() and verify that the names of the bind variables match
// what is expected, without duplicates (no error).
//-----------------------------------------------------------------------------
int dpiTest_1104_bindNamesNoDuplicatesSql(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select :a, :a, :xy, :xy from TestLongs", **bindNames;
    uint32_t numBindNames, *bindNameLengths;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBindCount(stmt, &numBindNames) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    bindNames = malloc(sizeof(const char *) * numBindNames);
    bindNameLengths = malloc(sizeof(uint32_t) * numBindNames);
    if (!bindNames || !bindNameLengths)
        return dpiTestCase_setFailed(testCase, "Out of memory!");
    if (dpiStmt_getBindNames(stmt, &numBindNames, bindNames,
            bindNameLengths) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, numBindNames, 2) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectStringEqual(testCase, bindNames[0],
            bindNameLengths[0], "A", 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectStringEqual(testCase, bindNames[1],
            bindNameLengths[1], "XY", 2) < 0)
        return DPI_FAILURE;
    free(bindNames);
    free(bindNameLengths);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1105_stmtInfoSelect()
//   Prepare any query; call dpiStmt_getInfo() and verify that the isQuery
// value in the dpiStmtInfo structure is set to 1 and all other values are set
// to zero and that the statementType value is set to DPI_STMT_TYPE_SELECT (no
// error).
//-----------------------------------------------------------------------------
int dpiTest_1105_stmtInfoSelect(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "select * from TestLongs";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_SELECT);
}


//-----------------------------------------------------------------------------
// dpiTest_1106_stmtInfoBegin()
//   Prepare any anonymous PL/SQL block without any declaration section; call
// dpiStmt_getInfo() and verify that the isPLSQL value in the dpiStmtInfo
// structure is set to 1 and all other values are set to zero and that the
// statementType value is set to DPI_STMT_TYPE_BEGIN (no error).
//-----------------------------------------------------------------------------
int dpiTest_1106_stmtInfoBegin(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "begin NULL; end;";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_BEGIN);
}


//-----------------------------------------------------------------------------
// dpiTest_1107_stmtInfoDeclare()
//   Prepare any anonymous PL/SQL block with a declaration section; call
// dpiStmt_getInfo() and verify that the isPLSQL value in the dpiStmtInfo
// structure is set to 1 and all other values are set to zero and that the
// statementType value is set to DPI_STMT_TYPE_DECLARE (no error).
//-----------------------------------------------------------------------------
int dpiTest_1107_stmtInfoDeclare(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "declare t number; begin NULL; end;";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_DECLARE);
}


//-----------------------------------------------------------------------------
// dpiTest_1108_stmtInfoInsert()
//   Prepare any insert statement; call dpiStmt_getInfo() and verify that the
// isDML value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_INSERT (no error).
//-----------------------------------------------------------------------------
int dpiTest_1108_stmtInfoInsert(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "insert into TestLongs values (:1, :2)";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_INSERT);
}


//-----------------------------------------------------------------------------
// dpiTest_1109_stmtInfoUpdate()
//   Prepare any update statement; call dpiStmt_getInfo() and verify that the
// isDML value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_UPDATE (no error).
//-----------------------------------------------------------------------------
int dpiTest_1109_stmtInfoUpdate(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "update TestLongs set longcol = :1 where intcol = :2";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_UPDATE);
}


//-----------------------------------------------------------------------------
// dpiTest_1110_stmtInfoDelete()
//   Prepare any delete statement; call dpiStmt_getInfo() and verify that the
// isDML value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_DELETE (no error).
//-----------------------------------------------------------------------------
int dpiTest_1110_stmtInfoDelete(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "delete TestLongs";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_DELETE);
}


//-----------------------------------------------------------------------------
// dpiTest_1111_stmtInfoCreate()
//   Prepare any create statement; call dpiStmt_getInfo() and verify that the
// isDDL value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_CREATE (no error).
//-----------------------------------------------------------------------------
int dpiTest_1111_stmtInfoCreate(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "create table Test (IntCol number(9))";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_CREATE);
}


//-----------------------------------------------------------------------------
// dpiTest_1112_stmtInfoDrop()
//   Prepare any drop statement; call dpiStmt_getInfo() and verify that the
// isDDL value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_DROP (no error).
//-----------------------------------------------------------------------------
int dpiTest_1112_stmtInfoDrop(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "drop table Test";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_DROP);
}


//-----------------------------------------------------------------------------
// dpiTest_1113_stmtInfoAlter()
//   Prepare any alter statement; call dpiStmt_getInfo() and verify that the
// isDDL value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_ALTER (no error).
//-----------------------------------------------------------------------------
int dpiTest_1113_stmtInfoAlter(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "alter table Test add X number";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_ALTER);
}


//-----------------------------------------------------------------------------
// dpiTest_1114_numQueryColumnsForQuery()
//   Prepare and execute any query; call dpiStmt_getNumQueryColumns() and
// verify that the value returned matches the number of columns expected to be
// returned by the query (no error).
//-----------------------------------------------------------------------------
int dpiTest_1114_numQueryColumnsForQuery(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select * from TestLongs";
    uint32_t numQueryColumns;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getNumQueryColumns(stmt, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTestCase_expectUintEqual(testCase, numQueryColumns, 2);
}


//-----------------------------------------------------------------------------
// dpiTest_1115_numQueryColumnsForNonQuery()
//   Prepare and execute any non-query; call dpiStmt_getNumQueryColumns() and
// verify that the value returned is 0 (no error).
//-----------------------------------------------------------------------------
int dpiTest_1115_numQueryColumnsForNonQuery(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "delete from TestLongs";
    uint32_t numQueryColumns;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getNumQueryColumns(stmt, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTestCase_expectUintEqual(testCase, numQueryColumns, 0);
}


//-----------------------------------------------------------------------------
// dpiTest_1116_queryInfoNonQuery()
//   Prepare and execute any non-query; call dpiStmt_getQueryInfo() for any
// non-zero position (error DPI-1028).
//-----------------------------------------------------------------------------
int dpiTest_1116_queryInfoNonQuery(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "delete from TestLongs";
    uint32_t numQueryColumns;
    dpiQueryInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiStmt_getQueryInfo(stmt, 1, &info);
    if (dpiTestCase_expectError(testCase,
            "DPI-1028: query position 1 is invalid") < 0)
        return DPI_FAILURE;
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1117_queryInfoMetadata()
//   Prepare and execute any query; call dpiStmt_getQueryInfo() for each of the
// columns and verify that the metadata returned is accurate (no error).
//-----------------------------------------------------------------------------
int dpiTest_1117_queryInfoMetadata(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *col1 = "INTCOL", *col2 = "STRINGCOL";
    const char *sql = "select * from TestTempTable";
    uint32_t numQueryColumns;
    dpiQueryInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // verify column 1
    if (dpiStmt_getQueryInfo(stmt, 1, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectStringEqual(testCase, info.name, info.nameLength,
            col1, strlen(col1)) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.oracleTypeNum,
            DPI_ORACLE_TYPE_NUMBER) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase,
            info.typeInfo.defaultNativeTypeNum, DPI_NATIVE_TYPE_INT64) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase,
            info.typeInfo.dbSizeInBytes, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase,
            info.typeInfo.clientSizeInBytes, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.precision, 9) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.scale, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.nullOk, 0) < 0)
        return DPI_FAILURE;

    // verify column 2
    if (dpiStmt_getQueryInfo(stmt, 2, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectStringEqual(testCase, info.name, info.nameLength,
            col2, strlen(col2)) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.oracleTypeNum,
            DPI_ORACLE_TYPE_VARCHAR) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase,
            info.typeInfo.defaultNativeTypeNum, DPI_NATIVE_TYPE_BYTES) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.dbSizeInBytes,
            100) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.clientSizeInBytes,
            100) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.precision, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.typeInfo.scale, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.nullOk, 1) < 0)
        return DPI_FAILURE;

    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1118_executeManyDefaultMode()
//   Prepare any DML statement; call dpiStmt_executeMany() with an array of
// data and mode set to DPI_MODE_EXEC_DEFAULT; call dpiStmt_getRowCounts()
// (error ORA-24349).
//-----------------------------------------------------------------------------
int dpiTest_1118_executeManyDefaultMode(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *insertSql = "insert into TestTempTable values (:1, :2)";
    const char *truncateSql = "truncate table TestTempTable";
    uint32_t numRows = 5, i, numQueryColumns, numRowCounts;
    dpiData *intData, *strData;
    dpiVar *intVar, *strVar;
    uint64_t *rowCounts;
    char buffer[100];
    dpiConn *conn;
    dpiStmt *stmt;

    // only supported in 12.1 and higher
    if (dpiTestCase_setSkippedIfVersionTooOld(testCase, 0, 12, 1) < 0)
        return DPI_FAILURE;

    // truncate table
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, truncateSql, strlen(truncateSql), NULL, 0,
            &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // prepare and bind insert statement
    if (dpiConn_prepareStmt(conn, 0, insertSql, strlen(insertSql), NULL, 0,
            &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64,
            numRows, 0, 0, 0, NULL, &intVar, &intData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 1, intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES,
            numRows, 100, 1, 0, NULL, &strVar, &strData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 2, strVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // populate some dummy data
    for (i = 0; i < numRows; i++) {
        intData[i].isNull = 0;
        intData[i].value.asInt64 = i + 1;
        sprintf(buffer, "Test data %d", i + 1);
        if (dpiVar_setFromBytes(strVar, i, buffer, strlen(buffer)) < 0)
            return dpiTestCase_setFailedFromError(testCase);
    }

    // perform execute many in default mode and attempt to get row counts
    if (dpiStmt_executeMany(stmt, DPI_MODE_EXEC_DEFAULT, numRows) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiStmt_getRowCounts(stmt, &numRowCounts, &rowCounts);
    if (dpiTestCase_expectError(testCase,
            "ORA-24349: Array DML row counts not available") < 0)
        return DPI_FAILURE;
    if (dpiVar_release(intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(strVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1119_executeManyArrayDmlRowcounts()
//   Prepare any DML statement; call dpiStmt_executeMany() with an array of
// data and mode set to DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS; call
// dpiStmt_getRowCounts() and verify that the row counts returned matches
// expectations; ensure that a value other than 1 is returned for at least one
// of the rowcounts (no error).
//-----------------------------------------------------------------------------
int dpiTest_1119_executeManyArrayDmlRowcounts(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *deleteSql = "delete from TestTempTable where IntCol < :1";
    const char *insertSql = "insert into TestTempTable values (:1, :2)";
    const char *truncateSql = "truncate table TestTempTable";
    uint32_t numRows = 5, i, numQueryColumns, numRowCounts;
    dpiData *intData, *strData;
    dpiVar *intVar, *strVar;
    uint64_t *rowCounts;
    char buffer[100];
    dpiConn *conn;
    dpiStmt *stmt;

    // only supported in 12.1 and higher
    if (dpiTestCase_setSkippedIfVersionTooOld(testCase, 0, 12, 1) < 0)
        return DPI_FAILURE;

    // truncate table
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, truncateSql, strlen(truncateSql), NULL, 0,
            &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // prepare and bind insert statement
    if (dpiConn_prepareStmt(conn, 0, insertSql, strlen(insertSql), NULL, 0,
            &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64,
            numRows, 0, 0, 0, NULL, &intVar, &intData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 1, intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES,
            numRows, 100, 1, 0, NULL, &strVar, &strData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 2, strVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // populate some dummy data
    for (i = 0; i < numRows; i++) {
        intData[i].isNull = 0;
        intData[i].value.asInt64 = i + 1;
        sprintf(buffer, "Dummy data %d", i + 1);
        if (dpiVar_setFromBytes(strVar, i, buffer, strlen(buffer)) < 0)
            return dpiTestCase_setFailedFromError(testCase);
    }

    // perform insert and verify all row counts are 1
    if (dpiStmt_executeMany(stmt, DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS,
            numRows) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getRowCounts(stmt, &numRowCounts, &rowCounts) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, numRowCounts, numRows) < 0)
        return DPI_FAILURE;
    for (i = 0; i < numRows; i++) {
        if (dpiTestCase_expectUintEqual(testCase, rowCounts[i], 1) < 0)
            return DPI_FAILURE;
    }
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(strVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // perform delete and verify row counts are as expected
    numRows = 2;
    if (dpiConn_prepareStmt(conn, 0, deleteSql, strlen(deleteSql), NULL, 0,
            &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64,
            numRows, 0, 0, 0, NULL, &intVar, &intData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 1, intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    intData[0].isNull = 0;
    intData[0].value.asInt64 = 2;
    intData[1].isNull = 0;
    intData[1].value.asInt64 = 5;
    if (dpiStmt_executeMany(stmt, DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS,
            numRows) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getRowCounts(stmt, &numRowCounts, &rowCounts) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, numRowCounts, numRows) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, rowCounts[0], 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, rowCounts[1], 3) < 0)
        return DPI_FAILURE;
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1120_bindCountDuplicateBindsSql()
//   Prepare any statement with at least one duplicate bind variable repeated
// in sql, call dpiStmt_getBindCount() and confirm that the value returned is
// the value expected (no error).
//-----------------------------------------------------------------------------
int dpiTest_1120_bindCountDuplicateBindsSql(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select :1, :1 from TestLongs";
    uint32_t count;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBindCount(stmt, &count) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTestCase_expectUintEqual(testCase, count, 2);
}


//-----------------------------------------------------------------------------
// dpiTest_1121_executeManyInvalidNumIters()
//   Prepare any non query with more than one bind variable; call
// dpiStmt_executeMany() with the parameter numIters set to a value that is
// greater than the maxArraySize for at least one of the variables that
// were bound to the statement (error DPI-1018).
//-----------------------------------------------------------------------------
int dpiTest_1121_executeManyInvalidNumIters(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "insert into TestLongs values (:1, :2)";
    dpiData *intdata, *strData;
    dpiVar *intVar, *strVar;
    uint32_t numIters = 4;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64,
            numIters, 0, 0, 0, NULL, &intVar, &intdata) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 1, intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES,
            numIters - 1, 100, 1, 0, NULL, &strVar, &strData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 2, strVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiStmt_executeMany(stmt, DPI_MODE_EXEC_DEFAULT, numIters);
    if (dpiTestCase_expectError(testCase,
            "DPI-1018: array size of 3 is too small") < 0)
        return DPI_FAILURE;
    if (dpiVar_release(intVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(strVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1122_bindCountDuplicateBindsPlsql()
//   Prepare any plsql statement with at least one duplicate bind variables
// call dpiStmt_getBindCount() and confirm that the value returned is the value
// expected (no error).
//-----------------------------------------------------------------------------
int dpiTest_1122_bindCountDuplicateBindsPlsql(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "begin select :1, :1 from TestLongs; end;";
    uint32_t count;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBindCount(stmt, &count) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTestCase_expectUintEqual(testCase, count, 1);
}


//-----------------------------------------------------------------------------
// dpiTest_1123_bindNamesNoDuplicatesPlsql()
//   Prepare any PL/SQL statement; call dpiStmt_getBindNames() and verify that
// the names of the bind variables match what is expected, with duplicates
// (bind variable name repeated in SQL text) (no error).
//-----------------------------------------------------------------------------
int dpiTest_1123_bindNamesNoDuplicatesPlsql(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "begin :c := :a1 * :a1 + :a2 * :a2; end;", **bindNames;
    uint32_t numBindNames, *bindNameLengths;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBindCount(stmt, &numBindNames) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    bindNames = malloc(sizeof(const char *) * numBindNames);
    bindNameLengths = malloc(sizeof(uint32_t) * numBindNames);
    if (!bindNames || !bindNameLengths)
        return dpiTestCase_setFailed(testCase, "Out of memory!");
    if (dpiStmt_getBindNames(stmt, &numBindNames, bindNames,
            bindNameLengths) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, numBindNames, 3) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectStringEqual(testCase, bindNames[0],
            bindNameLengths[0], "C", 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectStringEqual(testCase, bindNames[1],
            bindNameLengths[1], "A1", 2) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectStringEqual(testCase, bindNames[2],
            bindNameLengths[2], "A2", 2) < 0)
        return DPI_FAILURE;
    free(bindNames);
    free(bindNameLengths);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1124_closeStmtAndcallStmtPubFuncs()
//   Prepare any statement; call dpiStmt_close() and then call each public
// function for dpiStmt except for dpiStmt_addRef() and dpiStmt_release()
// (error DPI-1039).
//-----------------------------------------------------------------------------
int dpiTest_1124_closeStmtAndcallStmtPubFuncs(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select * from TestLongs";
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_close(stmt, NULL, 0) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTest__callFunctionsWithError(testCase, stmt,
            "DPI-1039: statement was already closed");
}


//-----------------------------------------------------------------------------
// dpiTest_1125_callStmtPubFuncsWithNull()
//   Call each of the public functions for dpiStmt with the stmt parameter
// set to NULL (error DPI-1002).
//-----------------------------------------------------------------------------
int dpiTest_1125_callStmtPubFuncsWithNull(dpiTestCase *testCase,
        dpiTestParams *params)
{
    return dpiTest__callFunctionsWithError(testCase, NULL,
            "DPI-1002: invalid dpiStmt handle");
}


//-----------------------------------------------------------------------------
// dpiTest_1126_verifyGetBindNamesWithLesserValue()
//   Prepare any statement; call dpiStmt_getBindNames() with the parameter
// numBindNames set to a value less than the number of bind names that are
// expected (error DPI-1018).
//-----------------------------------------------------------------------------
int dpiTest_1126_verifyGetBindNamesWithLesserValue(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *expectedError = "DPI-1018: array size of 1 is too small";
    const char *sql = "select :a, :xy from TestLongs", *bindName;
    uint32_t numBindNames = 1, bindNameLength;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiStmt_getBindNames(stmt, &numBindNames, &bindName, &bindNameLength);
    if (dpiTestCase_expectError(testCase, expectedError) < 0)
        return DPI_FAILURE;
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1127_rebindVariablesAndVerify()
//   Prepare and execute any statement with bind variables; rebind one or more
// of the bind variables with different variables and verify that the results
// match what is expected (no error).
//-----------------------------------------------------------------------------
int dpiTest_1127_rebindVariablesAndVerify(dpiTestCase *testCase,
        dpiTestParams *params)
{
    dpiData *inVarData1, *inVarData2, *inVarData3, *outData;
    const char *sql = "select :1 + :2 from dual";
    dpiVar *inVar1, *inVar2, *inVar3;
    dpiNativeTypeNum nativeTypeNum;
    uint32_t bufferRowIndex;
    dpiConn *conn;
    dpiStmt *stmt;
    int found;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_setFetchArraySize(stmt, 1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_UINT64, 1,
            0, 0, 0, NULL, &inVar1, &inVarData1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setUint64(inVarData1, 23);
    if (dpiStmt_bindByPos(stmt, 1, inVar1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_UINT64, 1,
            0, 0, 0, NULL, &inVar2, &inVarData2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setUint64(inVarData2, 33);
    if (dpiStmt_bindByPos(stmt, 2, inVar2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_UINT64, 1,
            0, 0, 0, NULL, &inVar3, &inVarData3) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setUint64(inVarData3, 130);
    if (dpiStmt_bindByPos(stmt, 1, inVar3) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_defineValue(stmt, 1, DPI_ORACLE_TYPE_NUMBER,
            DPI_NATIVE_TYPE_UINT64, 0, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase, "No rows found!");
    if (dpiStmt_getQueryValue(stmt, 1, &nativeTypeNum, &outData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, outData->value.asUint64,
            163) < 0)
        return DPI_FAILURE;
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(inVar1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(inVar2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(inVar3) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1128_verifyStoredProcWithBindVars()
//   Call any PL/SQL stored procedure and verify the arguments are passed
// correctly (no error).
//-----------------------------------------------------------------------------
int dpiTest_1128_verifyStoredProcWithBindVars(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "begin proc_Test(:1, :2, :3); end;";
    dpiData *inOutValue, *outValue, inValue;
    dpiVar *inOutVar, *outVar;
    uint32_t numQueryColumns;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setBytes(&inValue, "Test String", strlen("Test String"));
    if (dpiStmt_bindValueByPos(stmt, 1, DPI_NATIVE_TYPE_BYTES, &inValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64, 1,
            0, 0, 0, NULL, &inOutVar, &inOutValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setInt64(inOutValue, 10);
    if (dpiStmt_bindByPos(stmt, 2, inOutVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64, 1,
            0, 0, 0, NULL, &outVar, &outValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 3, outVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, 0, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, inOutValue->value.asInt64,
            110) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, outValue->value.asInt64, 11) < 0)
        return DPI_FAILURE;
    if (dpiVar_release(inOutVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiVar_release(outVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1129_verifyBindVarWithBatchErrorsExp()
//   Prepare and execute any statement with at least 17 bind variables with
// batch errors enabled and some batch errors expected (no error).
//-----------------------------------------------------------------------------
int dpiTest_1129_verifyBindVarWithBatchErrorsExp(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *truncateSql = "truncate table TestDataTypes";
    const char *insertSql = "insert into TestDataTypes (StringCol, "
            "UnicodeCol, FixedCharCol, FixedUnicodeCol, RawCol, FloatCol, "
            "DoublePrecCol, IntCol, NumberCol, DateCol, TimestampCol, "
            "TimestampTZCol, TimestampLTZCol, IntervalDSCol, IntervalYMCol, "
            "BinaryFltCol, BinaryDoubleCol, LongCol, UnconstrainedCol) values "
            "(:1, :2, :3, :4, '12AB', :5, :6, :7, :8, :9, :10, :11, :12, :13,"
            " :14, :15, :16, 1.454, :17)";
    uint32_t numQueryColumns, numRows = 3, numCols = 17, numErrs = 3, i, count;
    dpiErrorInfo errorInfo[3];
    char expectedError[512];
    dpiData *colData[17];
    dpiVar *colVar[17];
    dpiStmt *stmt;
    dpiConn *conn;

    // get connection
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;

    // truncate table
    if (dpiConn_prepareStmt(conn, 0, truncateSql, strlen(truncateSql), NULL, 0,
            &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, 0, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // create variables
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES,
            numRows, 100, 0, 0, NULL, &colVar[0], &colData[0]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NVARCHAR, DPI_NATIVE_TYPE_BYTES,
            numRows, 100, 0, 0, NULL, &colVar[1], &colData[1]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES,
            numRows, 100, 0, 0, NULL, &colVar[2], &colData[2]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NVARCHAR, DPI_NATIVE_TYPE_BYTES,
            numRows, 100, 0, 0, NULL, &colVar[3], &colData[3]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_DOUBLE,
            numRows, 0, 0, 0, NULL, &colVar[4], &colData[4]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_DOUBLE,
            numRows, 0, 0, 0, NULL, &colVar[5], &colData[5]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64,
            numRows, 0, 0, 0, NULL, &colVar[6], &colData[6]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_DOUBLE,
            numRows, 0, 0, 0, NULL, &colVar[7], &colData[7]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_DATE, DPI_NATIVE_TYPE_TIMESTAMP,
            numRows, 0, 0, 0, NULL, &colVar[8], &colData[8]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_TIMESTAMP,
            DPI_NATIVE_TYPE_TIMESTAMP, numRows, 0, 0, 0, NULL, &colVar[9],
            &colData[9]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_TIMESTAMP_TZ,
            DPI_NATIVE_TYPE_TIMESTAMP, numRows, 0, 0, 0, NULL, &colVar[10],
            &colData[10]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
            DPI_NATIVE_TYPE_TIMESTAMP, numRows, 0, 0, 0, NULL, &colVar[11],
            &colData[11]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_INTERVAL_DS,
            DPI_NATIVE_TYPE_INTERVAL_DS, numRows, 0, 0, 0, NULL, &colVar[12],
            &colData[12]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_INTERVAL_YM,
            DPI_NATIVE_TYPE_INTERVAL_YM, numRows, 0, 0, 0, NULL, &colVar[13],
            &colData[13]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NATIVE_FLOAT,
            DPI_NATIVE_TYPE_FLOAT, numRows, 0, 0, 0, NULL, &colVar[14],
            &colData[14]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NATIVE_DOUBLE,
            DPI_NATIVE_TYPE_DOUBLE, numRows, 0, 0, 0, NULL, &colVar[15],
            &colData[15]) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_DOUBLE,
            numRows, 0, 0, 0, NULL, &colVar[16], &colData[16]) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // prepare statement and perform binds
    if (dpiConn_prepareStmt(conn, 0, insertSql, strlen(insertSql),
            NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    for (i = 0; i < numCols; i++) {
        if (dpiStmt_bindByPos(stmt, i + 1, colVar[i]) < 0)
            return dpiTestCase_setFailedFromError(testCase);
    }

    // populate data
    for (i = 0; i < numRows; i++) {
        dpiData_setBytes(&colData[0][i], "string", strlen("string"));
        dpiData_setBytes(&colData[1][i], "unistring", strlen("unistring"));
        dpiData_setBytes(&colData[2][i], "fixedchar", strlen("fixedchar"));
        dpiData_setBytes(&colData[3][i], "fixedunichar",
                strlen("fixedunichar"));
        dpiData_setDouble(&colData[4][i], 1.25);
        dpiData_setDouble(&colData[5][i], 1.44);
        if (i < 2)
            dpiData_setInt64(&colData[6][i], 1);
        else dpiData_setInt64(&colData[6][i], 12345678910);
        dpiData_setDouble(&colData[7][i], 1.35);
        dpiData_setTimestamp(&colData[8][i], 2017, 6, 1, 2, 2, 1, 0, 0, 0);
        dpiData_setTimestamp(&colData[9][i], 2017, 6, 1, 2, 2, 1, 0, 0, 0);
        dpiData_setTimestamp(&colData[10][i], 2017, 6, 1, 2, 2, 1, 0, 0, 0);
        dpiData_setTimestamp(&colData[11][i], 2017, 6, 1, 2, 2, 1, 0, 0, 0);
        dpiData_setIntervalDS(&colData[12][i], 3, 2, 1, 1, 0);
        dpiData_setIntervalYM(&colData[13][i], 1, 1);
        dpiData_setDouble(&colData[14][i], 1.34);
        dpiData_setDouble(&colData[15][i], 1.95);
        dpiData_setDouble(&colData[16][i], 999);
    }

    // attempt to insert and validate errors received match expected errors
    if (dpiStmt_executeMany(stmt, DPI_MODE_EXEC_BATCH_ERRORS, numRows) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBatchErrorCount(stmt, &count) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getBatchErrors(stmt, numErrs, errorInfo) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    snprintf(expectedError, sizeof(expectedError),
            "ORA-00001: unique constraint (%.*s.TESTDATATYPES_PK) violated",
            params->mainUserNameLength, params->mainUserName);
    if (dpiTestCase_expectStringEqual(testCase, errorInfo[0].message,
            errorInfo[0].messageLength, expectedError,
            strlen(expectedError)) < 0)
        return DPI_FAILURE;
    strcpy(expectedError, "ORA-01438: value larger than specified precision "
            "allowed for this column");
    if (dpiTestCase_expectStringEqual(testCase, errorInfo[1].message,
            errorInfo[1].messageLength, expectedError,
            strlen(expectedError)) < 0)
        return DPI_FAILURE;

    // cleanup
    for (i = 0; i < numCols; i++) {
        if (dpiVar_release(colVar[i]) < 0)
            return dpiTestCase_setFailedFromError(testCase);
    }
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_1130_fetch1000ColsAndVerify()
//   Prepare and execute a query that fetches 1000 columns (no error)
//-----------------------------------------------------------------------------
int dpiTest_1130_fetch1000ColsAndVerify(dpiTestCase *testCase,
        dpiTestParams *params)
{
    uint32_t numQueryColumns;
    char sql[4000], *ptr;
    dpiConn *conn;
    dpiStmt *stmt;
    int i;

    // generate SQL statement
    strcpy(sql, "select ");
    ptr = sql + strlen(sql);
    for (i = 0; i < 1000; i++) {
        if (i > 0)
            *ptr++ = ',';
        ptr += sprintf(ptr, "%d", i + 1);
    }
    strcpy(ptr, " from dual");

    // execute it and verify the number of columns returned is as expected
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, DPI_MODE_EXEC_DEFAULT, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    return dpiTestCase_expectUintEqual(testCase, numQueryColumns, 1000);
}


//-----------------------------------------------------------------------------
// dpiTest_1131_stmtInfoMerge()
//   Prepare any merge statement; call dpiStmt_getInfo() and verify that the
// isDML value in the dpiStmtInfo structure is set to 1 and all other values
// are set to zero and that the statementType value is set to
// DPI_STMT_TYPE_MERGE (no error).
//-----------------------------------------------------------------------------
int dpiTest_1131_stmtInfoMerge(dpiTestCase *testCase, dpiTestParams *params)
{
    const char *sql = "merge into t1 a using t1 b on (a.intcol = 1) "
                      "when matched then update set a.longcol = 1 "
                      "when not matched then insert values(1,2)";
    dpiStmtInfo info;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_getInfo(stmt, &info) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, info.isQuery, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isPLSQL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDDL, 0) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isDML, 1) < 0)
        return DPI_FAILURE;
    if (dpiTestCase_expectUintEqual(testCase, info.isReturning, 0) < 0)
        return DPI_FAILURE;
    return dpiTestCase_expectUintEqual(testCase, info.statementType,
            DPI_STMT_TYPE_MERGE);
}


//-----------------------------------------------------------------------------
// dpiTest_1132_verifyStoredFuncWithBindVars()
//   Call any PL/SQL stored function and verify the arguments are passed
// correctly and the return value is valid (no error).
//-----------------------------------------------------------------------------
int dpiTest_1132_verifyStoredFuncWithBindVars(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "begin :1 := func_Test(:2, :3); end;";
    char *testStr = "Test String";
    dpiData *retValue, inputData;
    uint32_t numQueryColumns;
    dpiVar *retVar;
    dpiConn *conn;
    dpiStmt *stmt;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setBytes(&inputData, testStr, strlen(testStr));
    if (dpiStmt_bindValueByPos(stmt, 2, DPI_NATIVE_TYPE_BYTES, &inputData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setInt64(&inputData, 10);
    if (dpiStmt_bindValueByPos(stmt, 3, DPI_NATIVE_TYPE_INT64, &inputData) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiConn_newVar(conn, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_INT64, 1,
            0, 0, 0, NULL, &retVar, &retValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindByPos(stmt, 1, retVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, 0, &numQueryColumns) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, retValue->value.asInt64,
            strlen(testStr)+10) < 0)
        return DPI_FAILURE;
    if (dpiVar_release(retVar) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}



//-----------------------------------------------------------------------------
// main()
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    dpiTestSuite_initialize(1100);
    dpiTestSuite_addCase(dpiTest_1100_releaseTwice,
            "dpiStmt_release() twice");
    dpiTestSuite_addCase(dpiTest_1101_executeManyInvalidParams,
            "dpiStmt_executeMany() with invalid parameters");
    dpiTestSuite_addCase(dpiTest_1102_verifyCallStmtWorksAsExp,
            "dpiStmt_getInfo() for call statement");
    dpiTestSuite_addCase(dpiTest_1103_bindCountWithVarBinds,
            "dpiStmt_getBindCount() with variable binds");
    dpiTestSuite_addCase(dpiTest_1104_bindNamesNoDuplicatesSql,
            "dpiStmt_getBindNames() strips duplicates (SQL)");
    dpiTestSuite_addCase(dpiTest_1105_stmtInfoSelect,
            "dpiStmt_getInfo() for select statement");
    dpiTestSuite_addCase(dpiTest_1106_stmtInfoBegin,
            "dpiStmt_getInfo() for PL/SQL block starting with BEGIN");
    dpiTestSuite_addCase(dpiTest_1107_stmtInfoDeclare,
            "dpiStmt_getInfo() for PL/SQL block starting with DECLARE");
    dpiTestSuite_addCase(dpiTest_1108_stmtInfoInsert,
            "dpiStmt_getInfo() for insert statement");
    dpiTestSuite_addCase(dpiTest_1109_stmtInfoUpdate,
            "dpiStmt_getInfo() for update statement");
    dpiTestSuite_addCase(dpiTest_1110_stmtInfoDelete,
            "dpiStmt_getInfo() for delete statement");
    dpiTestSuite_addCase(dpiTest_1111_stmtInfoCreate,
            "dpiStmt_getInfo() for create statement");
    dpiTestSuite_addCase(dpiTest_1112_stmtInfoDrop,
            "dpiStmt_getInfo() for drop statement");
    dpiTestSuite_addCase(dpiTest_1113_stmtInfoAlter,
            "dpiStmt_getInfo() for alter statement");
    dpiTestSuite_addCase(dpiTest_1114_numQueryColumnsForQuery,
            "dpiStmt_getNumQueryColumn() for query");
    dpiTestSuite_addCase(dpiTest_1115_numQueryColumnsForNonQuery,
            "dpiStmt_getNumQueryColumn() for non-query");
    dpiTestSuite_addCase(dpiTest_1116_queryInfoNonQuery,
            "dpiStmt_getQueryInfo() for non-query");
    dpiTestSuite_addCase(dpiTest_1117_queryInfoMetadata,
            "dpiStmt_getQueryInfo() for query");
    dpiTestSuite_addCase(dpiTest_1118_executeManyDefaultMode,
            "dpiStmt_executeMany() without array DML row counts mode");
    dpiTestSuite_addCase(dpiTest_1119_executeManyArrayDmlRowcounts,
            "dpiStmt_executeMany() with array DML row counts mode");
    dpiTestSuite_addCase(dpiTest_1120_bindCountDuplicateBindsSql,
            "dpiStmt_getBindCount() with duplicate binds (SQL)");
    dpiTestSuite_addCase(dpiTest_1121_executeManyInvalidNumIters,
            "dpiStmt_executeMany() with invalid number of iterations");
    dpiTestSuite_addCase(dpiTest_1122_bindCountDuplicateBindsPlsql,
            "dpiStmt_getBindCount() with duplicate binds (PL/SQL)");
    dpiTestSuite_addCase(dpiTest_1123_bindNamesNoDuplicatesPlsql,
            "dpiStmt_getBindNames() strips duplicates (PL/SQL)");
    dpiTestSuite_addCase(dpiTest_1124_closeStmtAndcallStmtPubFuncs,
            "dpiStmt_close and call public functions");
    dpiTestSuite_addCase(dpiTest_1125_callStmtPubFuncsWithNull,
            "call pub functions with stmt set to NULL");
    dpiTestSuite_addCase(dpiTest_1126_verifyGetBindNamesWithLesserValue,
            "dpiStmt_getBindNames() with numBindNames set to less value");
    dpiTestSuite_addCase(dpiTest_1127_rebindVariablesAndVerify,
            "rebind the variable and verify");
    dpiTestSuite_addCase(dpiTest_1128_verifyStoredProcWithBindVars,
            "call PL/SQL procedure and verify the args are passed properly");
    dpiTestSuite_addCase(dpiTest_1129_verifyBindVarWithBatchErrorsExp,
            "bind many variables with batch errors enabled and verify");
    dpiTestSuite_addCase(dpiTest_1130_fetch1000ColsAndVerify,
            "execute query that fetches 1000 columns");
    dpiTestSuite_addCase(dpiTest_1131_stmtInfoMerge,
            "dpiStmt_getInfo() for merge statement");
    dpiTestSuite_addCase(dpiTest_1132_verifyStoredFuncWithBindVars,
            "call PL/SQL function & verify the args are passed properly");
    return dpiTestSuite_run();
}

